﻿using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Windows.Forms;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Fastcopy同步辅助器
{

    public partial class frm_Main : Form
    {

        #region 变量声明区

        string sourceDir = "";//源目录；
        string destDir = "";//目标目录.

        //创建“记录文件”
        string historyPath = Application.StartupPath + "\\History.ini";
        string fastcopyPath = Application.StartupPath + "\\FastCopy\\FastCopy.exe";
        string fasINI = Application.StartupPath + "\\FastcopyPath.ini";

        //历史记录
        List<string> strContents = new List<string>();
        string[] strlabel = {"[源目录]","[目标目录]"};

        TimeSpan startTime;


        Dictionary<int,string> theSameArryDestFiles;//相同的文件列表
        Dictionary<int,string> theSameDestFiles;
        int     cout=0;//相同文件个数


        List<string> arrySourceFiles;
        List<string> arryDestFiles;

        //保存着分离出来的所有文件的“文件名+扩展名”。
        List<string> safeFileNameS;
        List<string> safeFileNameD;

        //空文件总数计数器变量
        int intN = 0;
        //用于删除空文件夹的临时变量
        DirectoryInfo dir = null;

        #endregion


        #region “分析”按钮 相关自定义方法

        /// <summary>
        /// 开始分析的启动入口方法。
        /// </summary>
        private void myStart()
        {

            //假如分析正在进行，则取消分析。
            if ( btn_analyse.Text == "分析中…" )
            {
                myStartAnalyse.CancelAsync();
                myStartGetFiles.CancelAsync();
                myTimerRefesh_WasteTime.Stop();
                myTimerRefreshINFO.Stop();
            }
            else
            {

                startTime = DateTime.Now.TimeOfDay ;//记录下启动时间。
                myRefresh_Time();//开始更新耗时。
                btn_analyse.Text = "分析中…";

                //重置显示的内容，和写入的内容。
                rtb_FileList.Clear();
                theSameArryDestFiles = new Dictionary<int , string>();
                theSameDestFiles = new Dictionary<int , string>();

                cout = 0;

                rtb_FileList.AppendText("①┳开始读取两个目录下的所有文件（请稍等……）\n");
                rtb_FileList.Refresh();

                myStartGetFiles.RunWorkerAsync();

                myTimerRefreshINFO.Start();
                myTimerRefesh_WasteTime.Start();
            }

        }

        private void myStartGetFiles_DoWork(object sender , DoWorkEventArgs e)
        {
            myGetFilesINFO();
        }

        #region 获得文件系统信息

        /// <summary>
        /// 获取分析所必须的文件信息。
        /// </summary>
        private void myGetFilesINFO()
        {
            arrySourceFiles = new List<string>();
            arryDestFiles = new List<string>();
            myGetAllFiles(sourceDir , ref arrySourceFiles);
            myGetAllFiles(destDir , ref arryDestFiles);

            safeFileNameS = new List<string>();
            safeFileNameD = new List<string>();
            myFen_Li_Path(ref arrySourceFiles , ref safeFileNameS);
            myFen_Li_Path(ref arryDestFiles , ref safeFileNameD);
        }

        /// <summary>
        /// 获取目录下所有的文件列表。
        /// </summary>
        /// <param name="selectedPath">要获取的目录完整路径。</param>
        /// <param name="files">用于保存所有文件列表的变量。</param>
        private void myGetAllFiles(string selectedPath , ref List<string> files)
        {
            if ( myStartGetFiles.CancellationPending == true )
                return;//随时准备退出。
            try
            {
                files.AddRange(Directory.GetFiles(selectedPath , "*.*"));
            }
            catch
            {
                //目录异常。则不再往下执行获取目录命令。
                return;
            }

            string[] directories = Directory.GetDirectories(selectedPath);

            foreach ( string d in directories )
            {
                if ( myStartGetFiles.CancellationPending == true )
                    return;//随时准备退出。

                myGetAllFiles(d , ref files);
            }

        }

        /// <summary>
        /// 分离出的必要文件信息供分析相同文件的时候使用。
        /// </summary>
        private void myFen_Li_Path(ref List<string> arryfiles , ref List<string> safefiles)
        {            

            var safe = from f in arryfiles
                        let h = Path.GetFileName(f)
                        select h;
            safefiles.AddRange(safe);
        }

        #endregion

        #region 更新显示数据

        private void myTimer_Tick(object sender , EventArgs e)
        {
            myRefresh_INFO(false);
        }

        private void myTimerRefesh_WasteTime_Tick(object sender , EventArgs e)
        {
            myRefresh_Time();
        }

        /// <summary>
        /// 用于更新界面数据显示。每次调用此方法，就更新一次界面显示数据。
        /// </summary>
        private void myRefresh_INFO(bool isFinished)
        {
            
            rtb_FileList.Clear();
            if ( isFinished )
                rtb_FileList.AppendText("①┳开始读取两个目录下的所有文件\n");
            else
                rtb_FileList.AppendText("①┳开始读取两个目录下的所有文件（请稍等……）\n");
            if ( arrySourceFiles != null  )
            rtb_FileList.AppendText("  ┣[源  目录]: 总共 " + arrySourceFiles.Count  + " 个文件\n");
            if ( arryDestFiles != null  )
            rtb_FileList.AppendText("  ┗[目标目录]: 总共 " + arryDestFiles.Count  + " 个文件\n\n");
            rtb_FileList.Refresh();
        }

        private void myRefresh_Time()
        {
            TimeSpan wasteTime  = DateTime.Now.TimeOfDay.Subtract(startTime);

            string time = wasteTime.Hours + ":" + wasteTime.Minutes +":" + wasteTime.Seconds;
            this.Text = "My Fastcopy Helper" + "【总耗时: " + TimeSpan.Parse(time) + " 】";

        }

        #endregion

        private void myStartGetFiles_RunWorkerCompleted(object sender , RunWorkerCompletedEventArgs e)
        {


            myTimerRefreshINFO.Stop();
            myTimerRefreshINFO.Dispose();

            myRefresh_INFO(true);

            rtb_FileList.AppendText("②┳开始分析相同文件\n  ┣文件列表如下：\n");

            if ( myStartAnalyse.CancellationPending == true )
            {
                btn_analyse.Text = "分析";
                rtb_FileList.AppendText("分析 停止！");
                return;//退出执行。
            }
            
            myStartAnalyse.RunWorkerAsync();
            myStartGetFiles.Dispose();

        }


        private void myStartAnalyse_DoWork(object sender , System.ComponentModel.DoWorkEventArgs e)
        {
            myAnalyse();
        }

        /// <summary>
        /// 使用Source里面的文件，对比所有Dest里面的文件，看是否相同。
        /// </summary>
        private void myAnalyse()
        {
            //将所有的容量都设置为实际大小。因为泛型为null的也允许。
            arryDestFiles.TrimExcess();
            arrySourceFiles.TrimExcess();
            safeFileNameD.TrimExcess();
            safeFileNameS.TrimExcess();

            #region 相关思路

            //（TODO: ）速度问题还是有待解决！是在读取所有文件信息的时候慢？还是在分析相同文件的时候慢？
            //经过测试，发现是在分析相同文件的时候比较慢！看来用for 循环的性能很差！


            /* *****************************************************************************
             * 
             * 对比两个目录的文件名，有相同的话，单独提取出来
             * 
             * 对不同的文件（也就是“目标路径”里没有的文件），复制，到“目标路径”的新路径。
             * 
             * 对相同的文件，使用“目标目录”原始路径，复制，到新路径。
             * 
             * *****************************************************************************
             */

            #endregion

            int m=0,//源目录文件索引
                n=0;//目标目录索引
            
            foreach ( string var in safeFileNameS )
            {
                if ( myStartAnalyse.CancellationPending == true )
                    break;//随时准备停止操作

                n = 0;
                foreach ( string var2 in safeFileNameD )
                {

                    if ( myStartAnalyse.CancellationPending == true )
                        break;//随时准备停止操作

                    //用嵌套IF语句更贴近人的通常逻辑！【实践后的推荐！】
                    if ( var == var2 && var != "" && var2 != " " )
                    {

                        #region 相同文件判断语句
                        //////////////////////////////////////////////////
                        ///   分层递进式if判断，有助于性能部分提升！   ///
                        ///   把可能最耗时间的“比对操作”放到最后。   ///
                        ///   这样以后要减少或增加一个“比对项目”     ///
                        ///   只需要注释或重写一条语句即可。方便维护。 ///
                        //////////////////////////////////////////////////
                        //continue是直接结束本次循环，所以后面的n++不会执行，
                        //因此，只能在每次执行它之前，单独执行n++才行！

                        //将源目录对应到目标目录，直接合并成绝对路径\n
                        string DestFile = arrySourceFiles[ m ].Replace(sourceDir , destDir);

                        string DestDir = Path.GetDirectoryName(DestFile);

                        try
                        {

                            //判断目标文件是否存在，已经存在则忽略。
                            if ( File.Exists(DestFile) )
                            {
                                n++;
                                continue;
                            }
                            //判断最后修改时间也要相同。不同则忽略。
                            bool lWT = ( File.GetLastWriteTime(arryDestFiles[ n ]) == File.GetLastWriteTime(arrySourceFiles[ m ]) );
                            if ( !lWT )
                            {
                                n++;
                                continue;
                            }

                            //判断两个文件的大小也要相同。不同则忽略。
                            bool Fsize = ( new FileInfo(arryDestFiles[ n ]).Length == new FileInfo(arrySourceFiles[ m ]).Length );
                            if ( !Fsize )
                            {
                                n++;
                                continue;
                            }
                        }
                        catch
                        {
                            //出现异常的文件，忽略。
                            n++;
                            continue;
                        }

                        #endregion

                        cout++;

                        //将符合条件的相同文件，合并记录到一个变量中……
                        theSameArryDestFiles.Add(cout,arryDestFiles[ n ]);
                        theSameDestFiles.Add(cout,DestFile);

                        //将数据输出到列表框中……
                        SetText (arryDestFiles[ n ] + " \r\n\t——> " + DestDir + "\r\n");
                        rtb_FileList.ScrollToCaret();
                        
                    }
                    n++;
                }
                m++;
            }
        }

        #region 使用委托跨进程更新界面数据。

        private delegate void setTextDelegate(string text);
        private void SetText(string text)
        {
            // InvokeRequired required compares the thread ID of the
            // calling thread to the thread ID of the creating thread.
            // If these threads are different, it returns true.
            if ( rtb_FileList.InvokeRequired)
            {
                setTextDelegate  mySetTextD = new setTextDelegate(SetText);
                this.Invoke(mySetTextD  , new object[] { text });
            }
            else
            {
                rtb_FileList.AppendText(text);
            }
        }

        #endregion


        private void myStartAnalyse_RunWorkerCompleted(object sender , RunWorkerCompletedEventArgs e)
        {
            if ( e.Error != null )
            {
                rtb_FileList.AppendText("分析出错：" + e.Error.Message);
            }
            else if ( myStartAnalyse.CancellationPending == true )
            {
                rtb_FileList.AppendText("分析 停止！");
            }
            else
            {
                btn_Doit.Enabled = true;
                rtb_FileList.AppendText("\n  ┣分析完成！\n");
                rtb_FileList.AppendText("  ┣总计 " + cout.ToString() + " 个适合移动！");
                rtb_FileList.ScrollToCaret();
                
            }
            //一切都顺利的话

            myTimerRefesh_WasteTime.Stop();
            myTimerRefesh_WasteTime.Dispose();
            myRefresh_Time();

            btn_analyse.Text = "分析";
            myStartAnalyse.Dispose();
        }

        #endregion



        #region “执行”按钮 相关自定义方法

        /// <summary>
        /// 移动可以原地重用的文件，然后调用Fastcopy扫尾。
        /// </summary>
        private void myMoveTheSameFiles()
        {
            if ( cout == 0 )
                return;

            for(int i=1;i<=cout;i++)
            {

                string arryDestFiles = theSameArryDestFiles[ i ];
                string DestFile = theSameDestFiles[ i ];
                string DestDir =  Path.GetDirectoryName(DestFile);
                try
                {
                    Directory.CreateDirectory(DestDir);
                    File.Move(arryDestFiles , DestFile);
                }
                catch
                {
                    //出错则说明文件夹已经存在。
                }

            }
            rtb_FileList.AppendText("\n  ┗ 移动成功！");
            rtb_FileList.ScrollToCaret();

            myOpenFastcopy();//使用Fastcopy做最后扫尾工作。

        }

        private void myOpenFastcopy()
        {
            //最后启动Fastcopy来“同步”没有的文件！
            string comdline = "/cmd=sync /error_stop=FALSE /open_window /no_exec \""
                 + sourceDir
                 + "\" /to=\""
                 + destDir
                 + "\"";

            //默认使用程序根目录下fastcopy里程序文件。
            if ( File.Exists(fastcopyPath) )
            {
                Process.Start(fastcopyPath , comdline);
            }
            else if ( File.Exists(fasINI) )
            {
                fastcopyPath = File.ReadAllText(fasINI);
                myOpenFastcopy();
            }
            else if ( MessageBox.Show(this , "找不到Fastcopy.exe主程序文件！\n\n请自定义Fastcopy程序的路径。" , "— ！！！—" , MessageBoxButtons.OKCancel , MessageBoxIcon.Warning) == DialogResult.OK )
            {
                mySetFastcopyPath();
                myOpenFastcopy();
            }

        }

        private void mySetFastcopyPath()
        {
            //判断Fastcopy的路径：系统路径+程序路径+用户自定义
           DialogResult d = MessageBox.Show(this , "是否要自定义Fastcopy程序的路径？" , "Are You Sure?" , MessageBoxButtons.YesNo,MessageBoxIcon.Question);

           if ( d == DialogResult.Yes )
           {
               FolderBrowserDialog fdbd = new FolderBrowserDialog();
               fdbd.ShowNewFolderButton = false;
               fdbd.ShowDialog(this);
               if ( fdbd.SelectedPath == "" )
                   return;
               fastcopyPath = fdbd.SelectedPath + "\\fastcopy.exe";
               File.WriteAllText(fasINI , fastcopyPath); 

           }


        }


        /// <summary>
        /// 删除掉空的目录。
        /// </summary>
        private void myDeleteTheEmptyFolder(string searchPath)
        {
            List<string> strDirs = new List<string>();
            try
            {
                strDirs.AddRange(Directory.GetDirectories(searchPath , "*.*"));
            }
            catch
            {
                return;//the flolder is failed.
            }

            foreach ( string path in strDirs )
            {
                myDeleteTheEmptyFolder(path);
            }

            foreach ( string var in strDirs )
            {
                dir = new DirectoryInfo(var);
                try
                {
                    if (dir.FullName.Contains("svn"))
                        rtb_FileList.AppendText("\n" + "！. " + var + "\t禁止删除SVN版本控制数据库的空文件夹！");
                    else
                    {
                        dir.Delete();
                        intN++;
                        rtb_FileList.AppendText("\n" + intN.ToString() + ". " + var + "\t删除成功！");
                        rtb_FileList.ScrollToCaret();
                    }
                    
                }
                catch
                {
                    //目录不为空，无法删除。
                    continue;
                }

            }
        }

        #endregion


        #region 历史记录 功能区

        /// <summary>
        /// 提供初始化，和信息的添加。
        /// </summary>
        private void setFirst()
        {
            if ( strContents.Count == 0 )
            {
                strContents.AddRange(strlabel);
            }
            if ( cbb_source.Text != "" && !cbb_source.Items.Contains(cbb_source.Text) )
            {
                strContents.Insert(1 , cbb_source.Text);
            }
            if ( cbb_destDir.Text != "" && !cbb_destDir.Items.Contains(cbb_destDir.Text) )
            {

                strContents.Insert(strContents.IndexOf(strlabel[ 1 ]) + 1 , cbb_destDir.Text);
            }
            
        }

        /// <summary>
        /// 开始写入历史记录。
        /// </summary>
        /// <param name="Path"></param>
        public void Write(string Path)
        {
            setFirst();
            File.WriteAllLines(Path , strContents.ToArray() , Encoding.UTF8);
        }

        /// <summary>
        /// 将读取的所有内容载入变量中
        /// </summary>
        /// <param name="Path"></param>
        public void Read(string Path)
        {

            try
            {
                strContents.Clear();
                strContents.AddRange(File.ReadAllLines(Path));

                if ( strContents.Count != 0 )
                {
                    int index2 = strContents.IndexOf(strlabel[ 1 ]);
                    cbb_source.Items.Clear();
                    cbb_source.Items.AddRange(strContents.GetRange(1 , index2 - 1).ToArray());

                    cbb_destDir.Items.Clear();
                    cbb_destDir.Items.AddRange(strContents.GetRange(index2 + 1 , strContents.Count - index2-1).ToArray());
                }

            }
            catch
            {
                //strContents.Clear();
            }
        }


        #endregion


        #region 窗体部分按钮事件

        public frm_Main()
        {
            InitializeComponent();
        }

        private void btn_Quit_Click(object sender , EventArgs e)
        {
            this.Close();
        }

        private void btn_view_Click(object sender , EventArgs e)
        {
            if(File.Exists(historyPath))
            Process.Start(historyPath);
        }

        private void btn_Source_Click(object sender , EventArgs e)
        {
            //显示“选择文件夹”对话框
            FolderBrowserDialog fd = new FolderBrowserDialog();
            if ( fd.ShowDialog(this) == DialogResult.OK )
            {
                cbb_source.Text = fd.SelectedPath;
            }

        }

        private void btn_destDir_Click(object sender , EventArgs e)
        {
            //显示“选择文件夹”对话框
            FolderBrowserDialog fd = new FolderBrowserDialog();
            if ( fd.ShowDialog(this) == DialogResult.OK )
            {
                cbb_destDir.Text = fd.SelectedPath;
            }

        }

        private void cbb_source_DropDown(object sender , EventArgs e)
        {
            Read(historyPath);
        }

        private void cbb_destDir_DropDown(object sender , EventArgs e)
        {
            Read(historyPath);
        }

        private void cbb_source_TextChanged(object sender , EventArgs e)
        {
            sourceDir = cbb_source.Text;
            if ( cbb_destDir.Text != "" && cbb_source.Text != "" )
            {
                btn_analyse.Enabled = true;
                if ( cbb_source.Text == cbb_destDir.Text )
                    btn_analyse.Text = "删除空文件夹";
                else
                    btn_analyse.Text = "分析";
            }
            else
                btn_analyse.Enabled = false;
        }

        private void cbb_destDir_TextChanged(object sender , EventArgs e)
        {
            destDir = cbb_destDir.Text;
            if ( cbb_destDir.Text != "" && cbb_source.Text != "" )
            {
                btn_analyse.Enabled = true;
                if ( cbb_source.Text == cbb_destDir.Text )
                    btn_analyse.Text = "删除空文件夹";
                else
                    btn_analyse.Text = "分析";
            }
            else
                btn_analyse.Enabled = false;
        }

        private void btn_clear_Click(object sender , EventArgs e)
        {
            if ( File.Exists(historyPath) )
                File.Delete(historyPath);
        }

        private void btn_analyse_Click(object sender , EventArgs e)
        {
            intN = 0;
            Write(historyPath);
            if ( cbb_destDir.Text == cbb_source.Text )
            {
                DialogResult d = MessageBox.Show(
                    "此操作不可逆！！\n\n删除某些程序的空文件夹可能使其无法正常工作！！\n\n确定要删除？",
                    "警告！" ,
                    MessageBoxButtons.YesNo , MessageBoxIcon.Warning , MessageBoxDefaultButton.Button2);
                if ( d == DialogResult.No )
                    return;
                rtb_FileList.Clear();
                rtb_FileList.AppendText("███ 开始删除空文件夹：");
                btn_analyse.Text = "删除中…";
                myDeleteTheEmptyFolder(cbb_source.Text);
                rtb_FileList.AppendText("\n███ 总共 " + intN + " 个空文件夹删除成功！！");
                rtb_FileList.ScrollToCaret();
                btn_analyse.Text = "分析";
            }
            else
                myStart();
        }

        private void btn_Doit_Click(object sender , EventArgs e)
        {
            myMoveTheSameFiles();
            rtb_FileList.AppendText("\n\n③┳开始清理空文件夹……！");
            myDeleteTheEmptyFolder(destDir);
            rtb_FileList.AppendText("\n总共" + intN.ToString() + "个空文件夹清理完毕！");
            rtb_FileList.ScrollToCaret();
        }

        private void btn_SheZhi_Click(object sender , EventArgs e)
        {
            mySetFastcopyPath();
        }
        #endregion

        private void cbb_source_DragEnter(object sender , DragEventArgs e)
        {
            e.Effect = DragDropEffects.All;
            ( (ComboBox)sender ).Text = ( (string[])e.Data.GetData("FileNameW") )[ 0 ];
        }

        private void rtb_FileList_LinkClicked(object sender , LinkClickedEventArgs e)
        {
            try
            {
                Process.Start(e.LinkText);
            }
            catch
            {
            }
        }



    }
}
